package com.yupi.picturebackend.manager.websocket;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.yupi.picturebackend.manager.disruptor.PictureEditEvent;
import com.yupi.picturebackend.manager.disruptor.PictureEditEventProducer;
import com.yupi.picturebackend.manager.websocket.model.PictureEditActionEnum;
import com.yupi.picturebackend.manager.websocket.model.PictureEditMessageTypeEnum;
import com.yupi.picturebackend.manager.websocket.model.PictureEditRequestMessage;
import com.yupi.picturebackend.manager.websocket.model.PictureEditResponseMessage;
import com.yupi.picturebackend.model.entity.User;
import com.yupi.picturebackend.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 图片编辑处理器
 */
@Component
@Slf4j
public class PictureEditHandler extends TextWebSocketHandler {

    // 保存图片的编辑状态，key：pictureId，value：当前正在编辑的用户Id
    private final Map<Long,Long> pictureEditingUsers = new ConcurrentHashMap<>();
    // 保存所有连接回话，key：pictureId，value：每个用户会话
    // 使用ConcurrentHashMap，并发的hashMap，保证线程安全
    private final Map<Long, Set<WebSocketSession>> pictureSessions = new ConcurrentHashMap<>();

    @Resource
    private UserService userService;
    @Resource
    @Lazy
    private PictureEditEventProducer pictureEditEventProducer;

    /**
     * 连接建立成功后
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        super.afterConnectionEstablished(session);
        // 获取图片 id，用户
        Long pictureId = (Long) session.getAttributes().get("pictureId");
        User user = (User) session.getAttributes().get("user");
        // 保存会话到图片的会话集合中
        pictureSessions.putIfAbsent(pictureId,ConcurrentHashMap.newKeySet());// 如果不存在，创建一个空的集合
        pictureSessions.get(pictureId).add(session);
        // 构造相应，发送加入编辑信息通知
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue());
        pictureEditResponseMessage.setMessage("用户 " + user.getUserName() + " 进入编辑状态");
        pictureEditResponseMessage.setUser(userService.getUserVO(user));
        // 广播给所有用户
        broadcastToPicture(pictureId, pictureEditResponseMessage);
    }

    /**
     * 收到前端的消息，根据消息类型处理消息
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        super.handleTextMessage(session, message);
        // 将消息解析为 PictureEditMessage
        PictureEditRequestMessage pictureEditRequestMessage = JSONUtil.toBean(message.getPayload(), PictureEditRequestMessage.class);
//        String type = pictureEditRequestMessage.getType();
//        PictureEditMessageTypeEnum pictureEditMessageTypeEnum = PictureEditMessageTypeEnum.valueOf(type);

        // 从 Session 属性中获取公共参数
        Map<String, Object> attributes = session.getAttributes();
        User user = (User) attributes.get("user");
        Long pictureId = (Long) attributes.get("pictureId");

        // 生成消息到disruptor队列中
        pictureEditEventProducer.publishEvent(pictureEditRequestMessage,session,user,pictureId);
        // 调用对应的消息处理方法
//        switch (pictureEditMessageTypeEnum) {
//            case ENTER_EDIT:
//                handleEnterEditMessage(pictureEditRequestMessage, session, user, pictureId);
//                break;
//            case EDIT_ACTION:
//                handleEditActionMessage(pictureEditRequestMessage, session, user, pictureId);
//                break;
//            case EXIT_EDIT:
//                handleExitEditMessage(pictureEditRequestMessage, session, user, pictureId);
//                break;
//            default:
//                PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
//                pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.ERROR.getValue());
//                pictureEditResponseMessage.setMessage("消息类型错误");
//                pictureEditResponseMessage.setUser(userService.getUserVO(user));
//                session.sendMessage(new TextMessage(JSONUtil.toJsonStr(pictureEditResponseMessage)));
//        }
    }

    /**
     * 连接关闭后
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, org.springframework.web.socket.CloseStatus status) throws Exception {
        super.afterConnectionClosed(session, status);
        // 从 Session 属性中获取公共参数
        Map<String, Object> attributes = session.getAttributes();
        User user = (User) attributes.get("user");
        Long pictureId = (Long) attributes.get("pictureId");
        // 移除当前用户编辑状态
        handleExitEditMessage(null, session, user, pictureId);
        // 删除会话
        Set<WebSocketSession> sessionSet = pictureSessions.get(pictureId);
        if (sessionSet != null) {
            sessionSet.remove(session);
            // 如果图片已不存在会话连接，则删除图片会话集合
            if (sessionSet.isEmpty()) {
                pictureSessions.remove(pictureId);
            }
        }
        // 响应
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue());
        String message = String.format("%s离开编辑", user.getUserName());
        pictureEditResponseMessage.setMessage(message);
        pictureEditResponseMessage.setUser(userService.getUserVO(user));
        broadcastToPicture(pictureId, pictureEditResponseMessage);
    }

    /**
     * 广播图片编辑信息给旗下的用户
     * @param pictureId                  图片 id
     * @param pictureEditResponseMessage 图片编辑信息
     * @param excludeSession             被排除的用户会话
     * @throws Exception
     */
    private void broadcastToPicture(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage, WebSocketSession excludeSession) throws Exception{
        // 获取图片下所有的会话集合
        Set<WebSocketSession> sessionSet = pictureSessions.get(pictureId);
        // 如果会话集合不为空
        if (CollUtil.isNotEmpty(sessionSet)){

            // region 创建处理前端js精度丢失的处理器,在JsonConfig中复制
            // 创建 ObjectMapper
            ObjectMapper objectMapper = new ObjectMapper();
            // 配置序列化：将 Long 类型转为 String，解决丢失精度问题
            SimpleModule module = new SimpleModule();
            module.addSerializer(Long.class, ToStringSerializer.instance);
            module.addSerializer(Long.TYPE, ToStringSerializer.instance); // 支持 long 基本类型
            objectMapper.registerModule(module);
            // endregion

            // 将 PictureEditResponseMessage 对象转换为 JSON 字符串
            String message = objectMapper.writeValueAsString(pictureEditResponseMessage);
            TextMessage textMessage = new TextMessage(message);
            // 遍历会话集合，发送消息
            for (WebSocketSession webSocketSession : sessionSet){
                // 排除掉的会话不发送消息
                if (excludeSession != null && excludeSession.equals(webSocketSession)){
                    // 跳过当前循环
                    continue;
                }
                if (webSocketSession.isOpen()){
                    webSocketSession.sendMessage(textMessage);
                }
            }
        }
    }

    /**
     * 广播图片编辑信息给旗下的用户（无需排除）
     * @param pictureId
     * @param pictureEditResponseMessage
     * @throws Exception
     */
    public void broadcastToPicture(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage) throws Exception{
        broadcastToPicture(pictureId, pictureEditResponseMessage, null);
    }

    /**
     * 处理进入编辑状态的消息
     * @param pictureEditRequestMessage
     * @param session
     * @param user
     * @param pictureId
     * @throws Exception
     */
    public void handleEnterEditMessage(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws Exception{
        // 没有用户在编辑状态，才能进入编辑状态
        if (!pictureEditingUsers.containsKey(pictureId)){
            // 设置用户正在编辑该图片
            pictureEditingUsers.put(pictureId, user.getId());
            // 构造相应，发送开始编辑信息通知
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.ENTER_EDIT.getValue());
            pictureEditResponseMessage.setMessage("用户 " + user.getUserName() + " 开始编辑图片");
            pictureEditResponseMessage.setUser(userService.getUserVO(user));
            // 广播给所有用户
            broadcastToPicture(pictureId, pictureEditResponseMessage);
        }
    }

    /**
     * 处理编辑操作的消息
     * @param pictureEditRequestMessage
     * @param session
     * @param user
     * @param pictureId
     * @throws Exception
     */
    public void handleEditActionMessage(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws Exception{
        // 获取编辑用户
        Long editingUserId = pictureEditingUsers.get(pictureId);
        // 获取编辑操作
        String editAction = pictureEditRequestMessage.getEditAction();
        PictureEditActionEnum editActionEnum = PictureEditActionEnum.getEnumByValue(editAction);
        if (editActionEnum == null){
            log.error("编辑操作错误");
            return;
        }
        // 确定是当前编辑者
        if (editingUserId != null && editingUserId.equals(user.getId())){
            // 构造相应，发送具体操作通知
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.EDIT_ACTION.getValue());
            String message = String.format("%s执行%s", user.getUserName(), editActionEnum.getText());
            pictureEditResponseMessage.setMessage(message);
            // 设置编辑动作
            pictureEditResponseMessage.setEditAction(editAction);
            pictureEditResponseMessage.setUser(userService.getUserVO(user));
            // 广播给除了编辑者外的所有用户
            broadcastToPicture(pictureId, pictureEditResponseMessage, session);
        }
    }

    /**
     * 处理退出编辑状态的消息
     * @param pictureEditRequestMessage
     * @param session
     * @param user
     * @param pictureId
     * @throws Exception
     */
    public void handleExitEditMessage(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws Exception{
        // 获取编辑用户
        Long editingUserId = pictureEditingUsers.get(pictureId);
        // 确定是当前编辑者
        if (editingUserId != null && editingUserId.equals(user.getId())){
            // 将该用户退出编辑状态
            pictureEditingUsers.remove(pictureId);
            // 构造相应，发送退出编辑状态通知
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.EXIT_EDIT.getValue());
            pictureEditResponseMessage.setMessage("用户 " + user.getUserName() + " 退出编辑图片");
            pictureEditResponseMessage.setUser(userService.getUserVO(user));
            // 广播给所有用户
            broadcastToPicture(pictureId, pictureEditResponseMessage);
        }
    }
}
